/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkMetaImageIO.h"

#include "itkImageIOFactory.h" // required to instantiate an instance of ImageIOBase

// Specific ImageIO test

// Macro to check that two arrays have the same size at compile time. It doesn't compile if they don't
// as it tries to create an array of size(-1)
// https://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/
#define CHECK_ARRAYS_HAVE_SAME_SIZE_AT_COMPILE_TIME(array1, array2)\
 ((void)sizeof(char[1 - 2*!!( sizeof(array1) / sizeof(*array1) - sizeof(array2) / sizeof(*array2) )]))

int itkImageIOBaseTest( int , char * [] )
{
  itk::MetaImageIO::Pointer reader = itk::MetaImageIO::New();
  reader->SetNumberOfDimensions(3);

  bool gotException = false;
  try
    {
    reader->SetDimensions(3,1);
    }
  catch ( itk::ExceptionObject& e )
    {
    std::cerr << "Caught expected exception " << e << std::endl;
    gotException = true;
    }
  if (!gotException)
    {
    std::cerr << "Failed to catch expected exception in method SetDimensions"
              << std::endl;
    return EXIT_FAILURE;
    }

  gotException = false;
  try
    {
    reader->SetOrigin(3,1.0);
    }
  catch ( itk::ExceptionObject& e )
    {
    std::cerr << "Caught expected exception " << e << std::endl;
    gotException = true;
    }
  if (!gotException)
    {
    std::cerr << "Failed to catch expected exception in method SetOrigin"
              << std::endl;
    return EXIT_FAILURE;
    }

  gotException = false;
  try
    {
    reader->SetSpacing(3,1.0);
    }
  catch ( itk::ExceptionObject& e )
    {
    std::cerr << "Caught expected exception " << e << std::endl;
    gotException = true;
    }
  if (!gotException)
    {
    std::cerr << "Failed to catch expected exception in method SetSpacing"
              << std::endl;
    return EXIT_FAILURE;
    }

  gotException = false;
  try
    {
    std::vector<double> direction(3);
    direction[0] = 1.0;
    direction[1] = 1.0;
    direction[2] = 1.0;
    reader->SetDirection(3,direction);
    }
  catch ( itk::ExceptionObject& e )
    {
    std::cerr << "Caught expected exception " << e << std::endl;
    gotException = true;
    }
  if (!gotException)
    {
    std::cerr << "Failed to catch expected exception in method SetDirection"
              << std::endl;
    return EXIT_FAILURE;
    }

  gotException = false;
  try
    {
    vnl_vector<double> direction(3);
    direction[0] = 1.0;
    direction[1] = 1.0;
    direction[2] = 1.0;
    reader->SetDirection(3,direction);
    }
  catch ( itk::ExceptionObject& e )
    {
    std::cerr << "Caught expected exception " << e << std::endl;
    gotException = true;
    }
  if (!gotException)
    {
    std::cerr << "Failed to catch expected exception in method SetDirection"
              << std::endl;
    return EXIT_FAILURE;
    }
  { // Test string <-> type conversions
  itk::ImageIOBase::IOComponentType listComponentType[] = {itk::ImageIOBase::UNKNOWNCOMPONENTTYPE,
                                                           itk::ImageIOBase::UCHAR, itk::ImageIOBase::CHAR,
                                                           itk::ImageIOBase::USHORT, itk::ImageIOBase::SHORT,
                                                           itk::ImageIOBase::UINT, itk::ImageIOBase::INT,
                                                           itk::ImageIOBase::ULONG, itk::ImageIOBase::LONG,
                                                           itk::ImageIOBase::ULONGLONG, itk::ImageIOBase::LONGLONG,
                                                           itk::ImageIOBase::FLOAT, itk::ImageIOBase::DOUBLE};
  const char* listComponentTypeString[] = {"unknown", "unsigned_char", "char", "unsigned_short", "short", "unsigned_int", "int",
                                           "unsigned_long", "long", "unsigned_long_long", "long_long", "float", "double"};
  itk::ImageIOBase::IOPixelType listIOPixelType[] = {itk::ImageIOBase::UNKNOWNPIXELTYPE,itk::ImageIOBase::SCALAR,
                                                     itk::ImageIOBase::RGB, itk::ImageIOBase::RGBA,
                                                     itk::ImageIOBase::OFFSET, itk::ImageIOBase::VECTOR,
                                                     itk::ImageIOBase::POINT, itk::ImageIOBase::COVARIANTVECTOR,
                                                     itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR,
                                                     itk::ImageIOBase::DIFFUSIONTENSOR3D, itk::ImageIOBase::COMPLEX,
                                                     itk::ImageIOBase::FIXEDARRAY, itk::ImageIOBase::MATRIX};
  const char* listIOPixelTypeString[] = {"unknown", "scalar", "rgb", "rgba", "offset", "vector", "point", "covariant_vector",
                                         "symmetric_second_rank_tensor", "diffusion_tensor_3D", "complex",
                                         "fixed_array", "matrix"};
  CHECK_ARRAYS_HAVE_SAME_SIZE_AT_COMPILE_TIME(listComponentTypeString, listComponentType);
  CHECK_ARRAYS_HAVE_SAME_SIZE_AT_COMPILE_TIME(listIOPixelType, listIOPixelTypeString);
  size_t listComponentSize = sizeof(listComponentType) / sizeof(*listComponentType);
  size_t listPixelSize = sizeof(listIOPixelType) / sizeof(*listIOPixelType);
    {  // Test the static version of the string <-> type conversions
    for(size_t i = 0; i < listComponentSize; ++i)
      {
      std::string componentTypeString = itk::ImageIOBase::GetComponentTypeAsString(listComponentType[i]);
      if(componentTypeString.compare(listComponentTypeString[i]) != 0)
        {
        std::cerr << "GetComponentTypeAsString("<<listComponentType[i]<<") should return '"<<listComponentTypeString[i]<<"'"
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listPixelSize; ++i)
      {
      std::string pixelTypeString = itk::ImageIOBase::GetPixelTypeAsString(listIOPixelType[i]);
      if(pixelTypeString.compare(listIOPixelTypeString[i]) != 0)
        {
        std::cerr << "GetPixelTypeAsString("<< listIOPixelType[i] <<") should return '"<< listIOPixelTypeString[i] <<"'"
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listComponentSize; ++i)
      {
      itk::ImageIOBase::IOComponentType componentType = itk::ImageIOBase::GetComponentTypeFromString(listComponentTypeString[i]);
      if(componentType != listComponentType[i])
        {
        std::cerr << "GetComponentTypeFromString('"<< listComponentTypeString[i] <<"') should return " << listComponentType[i]
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listPixelSize; ++i)
      {
      itk::ImageIOBase::IOPixelType pixelType = itk::ImageIOBase::GetPixelTypeFromString(listIOPixelTypeString[i]);
      if(pixelType != listIOPixelType[i])
        {
        std::cerr << "GetPixelTypeFromString('"<< listIOPixelTypeString[i] << "') should return " << listIOPixelType[i]
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    }// end Test the static version of the string <-> type conversions

    // Test the non-static version of the string <-> type conversions
    {
    // Create an instance of ImageIOBase. It does not matter that 'test' is not a valid image to read,
    // we just want the ImageIOBase object.
    itk::ImageIOBase::Pointer imageIOBase = itk::ImageIOFactory::CreateImageIO(
      "test", itk::ImageIOFactory::ReadMode);
    for(size_t i = 0; i < listComponentSize; ++i)
      {
      std::string componentTypeString = imageIOBase->GetComponentTypeAsString(listComponentType[i]);
      if(componentTypeString.compare(listComponentTypeString[i]) != 0)
        {
        std::cerr << "GetComponentTypeAsString("<< listComponentType[i] <<") should return '" << listComponentTypeString[i] <<"'"
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listPixelSize; ++i)
      {
      std::string pixelTypeString = imageIOBase->GetPixelTypeAsString(listIOPixelType[i]);
      if(pixelTypeString.compare(listIOPixelTypeString[i]) != 0)
        {
        std::cerr << "GetPixelTypeAsString("<< listIOPixelType[i] <<") should return " << listIOPixelTypeString[i]
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listComponentSize; ++i)
      {
      itk::ImageIOBase::IOComponentType componentType = imageIOBase->GetComponentTypeFromString(listComponentTypeString[i]);
      if(componentType != listComponentType[i])
        {
        std::cerr << "GetComponentTypeFromString('" << listComponentTypeString[i] << "') should return " << listComponentType[i]
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    for(size_t i = 0; i < listPixelSize; ++i)
      {
      itk::ImageIOBase::IOPixelType pixelType = imageIOBase->GetPixelTypeFromString(listIOPixelTypeString[i]);
      if(pixelType != listIOPixelType[i])
        {
        std::cerr << "GetPixelTypeFromString('"<< listIOPixelTypeString[i] <<"') should return " << listIOPixelType[i]
                  << std::endl;
        return EXIT_FAILURE;
        }
      }
    }// end Test the non-static version of the string <-> type conversions
  }
  return EXIT_SUCCESS;
}
